package com.tmsps.ne4Weixin.config;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.Observable;
import java.util.concurrent.atomic.AtomicBoolean;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.tmsps.ne4Weixin.handel.ApiConfigChangeHandle;
import com.tmsps.ne4Weixin.utils.HttpClient;
import com.tmsps.ne4Weixin.utils.SignUtil;
import com.tmsps.ne4Weixin.utils.StrUtil;

/**
 * @author zhangwei 396033084@qq.com WxConfig
 */
public class WxConfig extends Observable implements Serializable {
	private static final long serialVersionUID = 1L;

	private static final Logger log = LoggerFactory.getLogger(WxConfig.class);

	private final String appid;
	private final String secret;
	private final String encodingaeskey;
	private final boolean issafe;
	private final String token;
	// 商户ID
	private final String mch_id;
	private final String payAPI;
	private final int expires_time;
	// 拓展MAP
	private final Map<String, Object> extendMap = new HashMap<String, Object>();

	/**
	 * 其他参数配置
	 */
	private String accessToken;
	private final AtomicBoolean accessTokenTag = new AtomicBoolean(false);
	private Long accessTokenExpireTime = System.currentTimeMillis();
	// js sdk token
	private String jsAccessToken;
	private final AtomicBoolean jsAccessTokenTag = new AtomicBoolean(false);
	private Long jsAccessTokenExpireTime = System.currentTimeMillis();

	public WxConfig(String appid, String secret, String encodingaeskey, boolean issafe, String token) {
		this(appid, secret, encodingaeskey, issafe, token, 3600);
	}
	public WxConfig(String appid, String secret, String encodingaeskey, boolean issafe, String token , int expires_time) {
		this.appid = appid;
		this.secret = secret;
		this.encodingaeskey = encodingaeskey;
		this.issafe = issafe;
		this.token = token;
		this.mch_id = null;
		this.payAPI = null;
		this.expires_time = expires_time;
	}

	public WxConfig(String appid, String secret, String encodingaeskey, boolean issafe, String token, String mch_id, String payAPI) {
		this(appid, secret, encodingaeskey, issafe, token, mch_id, payAPI, 3600);
	}
	
	public WxConfig(String appid, String secret, String encodingaeskey, boolean issafe, String token, String mch_id, String payAPI, int expires_time) {
		this.appid = appid;
		this.secret = secret;
		this.encodingaeskey = encodingaeskey;
		this.issafe = issafe;
		this.token = token;
		this.mch_id = mch_id;
		this.payAPI = payAPI;
		this.expires_time = expires_time;
	}

	public void set(String key, Object value) {
		extendMap.put(key, value);
	}

	public Object get(String key) {
		return extendMap.get(key);
	}

	public String getAppid() {
		if (SignUtil.isNull(appid)) {
			log.error("appid can not be null or empty");
			return null;
		}
		return appid;
	}

	public String getSecret() {
		if (SignUtil.isNull(secret)) {
			log.error("secret can not be null or empty");
			return null;
		}
		return secret;
	}

	public String getEncodingaeskey() {
		if (SignUtil.isNull(encodingaeskey)) {
			log.error("encodingaeskey can not be null or empty");
			return null;
		}
		return encodingaeskey;
	}

	public boolean getIssafe() {
		return issafe;
	}

	public String getToken() {
		if (SignUtil.isNull(token)) {
			log.error("token can not be null or empty");
			return null;
		}
		return token;
	}

	public String getMch_id() {
		return mch_id;
	}

	public String getPayAPI() {
		return payAPI;
	}

	// 初始化 AccessToken
	public synchronized void initAccessToken() {
		log.debug("---===   start initAccessToken  ===---");
		this.accessTokenTag.set(true);
		try {
			final String url = "https://api.weixin.qq.com/cgi-bin/token";
			log.debug("start to request url {}", url);
			Map<String, String> params = new HashMap<String, String>();
			params.put("grant_type", "client_credential");
			params.put("appid", getAppid());
			params.put("secret", getSecret());
			log.debug("with {}", JSON.toJSON(params));
			String resp = HttpClient.httpGet(url, params);
			log.debug("get respose is {}", resp);
			JSONObject result = JSONObject.parseObject(resp);
			log.debug("getAccessToken result is {}", result.toString());

			this.accessToken = result.getString("access_token");
			this.accessTokenExpireTime = System.currentTimeMillis() + result.getLongValue("expires_in") * 1000;

		} catch (Exception e) {
			log.error("initAccessToken ERROR :{}", e.toString());
		} finally {
			this.accessTokenTag.set(false);
		}
		log.debug("accessTokenExpireTime is {}", accessTokenExpireTime);
		log.debug("---===   over initAccessToken  ===---");
	}

	public String getAccessToken() {
		if (StrUtil.isBlank(accessToken)) {
			log.debug("accessToken is blank initAccessToken()");
			initAccessToken();
		} else {
			long diffTime = accessTokenExpireTime - System.currentTimeMillis();
			log.debug("currentTime is {}, accessTokenExpireTime is {}, diffTime is {}", String.valueOf(System.currentTimeMillis()), String.valueOf(accessTokenExpireTime), String.valueOf(diffTime));
			if (diffTime < expires_time * 1000 && this.accessTokenTag.compareAndSet(false, true)) {
				log.debug("accessToken is exceed the time limit, initAccessToken()");
				initAccessToken();
			}
		}
		return accessToken;
	}

	// 初始化 JsAccessToken
	public synchronized void initJsAccessToken() {
		this.jsAccessTokenTag.set(true);
		final String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket";
		Map<String, String> params = new HashMap<String, String>();
		params.put("access_token", getAccessToken());
		params.put("type", "jsapi");
		JSONObject result = JSONObject.parseObject(HttpClient.httpGet(url, params));
		log.debug("getJsAccessToken result is {}", result.toString());
		if (result.getInteger("errcode") == 0) {
			this.jsAccessToken = result.getString("ticket");
			this.jsAccessTokenExpireTime = System.currentTimeMillis() + result.getLongValue("expires_in") * 1000;
		} else {
			log.error(result.getString("errmsg"));
		}
		this.jsAccessTokenTag.set(false);
	}

	/**
	 * @author zhangwei 396033084@qq.com
	 */
	public String getJsAccessToken() {
		if (StrUtil.isBlank(jsAccessToken)) {
			log.debug("jsAccessToken is blank initJsAccessToken()");
			this.initJsAccessToken();
		} else {
			long diffTime = jsAccessTokenExpireTime - System.currentTimeMillis();
			if (diffTime < expires_time * 1000 && this.jsAccessTokenTag.compareAndSet(false, true)) {
				log.debug("jsAccessToken is exceed the time limit, initJsAccessToken()");
				initJsAccessToken();
			}
		}
		return jsAccessToken;
	}

	 /**
     * 添加配置变化监听器
     *
     * @param handle 监听器
     */
    public void addHandle(final ApiConfigChangeHandle handle) {
        super.addObserver(handle);
    }

    /**
     * 移除配置变化监听器
     *
     * @param handle 监听器
     */
    public void removeHandle(final ApiConfigChangeHandle handle) {
        super.deleteObserver(handle);
    }

    /**
     * 移除所有配置变化监听器
     */
    public void removeAllHandle() {
        super.deleteObservers();
    }
	
	
}
